#ifndef ScriptDocumentOperation_h
#define ScriptDocumentOperation_h

#include "DocumentOperationFactory.h"
#include "SemanticDataCollector.h"
#include "InputSupplier.h"
#include "ScriptSemanticTokenLegend.h"
#include "simodo/inout/convert/functions.h"
#include "simodo/inout/token/TokenLocation.h"
#include "simodo/lsp-client/SimodoCommandResult.h"
#include "simodo/interpret/InterpretType.h"
#include "simodo/module/ModuleManagement.h"

inline const std::u16string execution_report = u"execution report";

class ScriptDocumentOperation : public simodo::lsp::DocumentOperation_interface
{
    friend class FuzeDocumentOperation;

    simodo::lsp::DocumentContext &      _doc;
    const DocumentOperationFactory &    _factory;
    std::string                         _languageId;

    simodo::inout::uri_index_t          _current_file_index = 0;

    simodo::inout::uri_set_t            _files;
    SemanticDataCollector               _semantic_data;

    std::shared_ptr<simodo::module::ModuleManagement> _module_management_last_instance;
                                        ///< Код внутренних (internal) функций хранится в module::ModuleManagement,
                                        ///< поэтому его необходимо сохранять до следующего вызова analyze

public:
    ScriptDocumentOperation(simodo::lsp::DocumentContext &doc, 
                            const DocumentOperationFactory & factory,
                            std::string  languageId);

    virtual bool analyze(const std::u16string & text, simodo::inout::Reporter_abstract & reporter) override;

    virtual bool checkDependency(const std::string & uri) const override;

    virtual simodo::variable::Value produceSimodoCommandResponse(const std::u16string & command_name, std::u16string text) const override;
    virtual simodo::variable::Value produceHoverResponse(const simodo::lsp::Position & pos) const override;
    virtual simodo::variable::Value produceGotoDeclarationResponse(const simodo::lsp::Position & pos) const override;
    virtual simodo::variable::Value produceGotoDefinitionResponse(const simodo::lsp::Position & pos) const override;
    virtual simodo::variable::Value produceCompletionResponse(const simodo::lsp::CompletionParams & completionParams) const override;
    virtual simodo::variable::Value produceSemanticTokensResponse() const override;
    virtual simodo::variable::Value produceDocumentSymbolsResponse() const override;

private:
    // analyze

    std::string getInitialContractFile() const;

    void findCurrentFileIndex();

    std::unique_ptr<simodo::inout::InputStreamSupplier_interface> getInputSupplier(const std::u16string & text) const {
        return std::make_unique<InputSupplier>(_doc.server(), _doc.file_name(), text);
    }

    /// Создаёт семантический анализатор/исполнитель и анализирует/выполняет код текущего файла
    /// \return std::pair, состоящая из ссылки на созданный семантический анализатор и результата выполнения (успешно ли)
    std::pair<std::shared_ptr<simodo::module::ModuleManagement>, bool> executeModuleManager(
            simodo::inout::InputStreamSupplier_interface &       input_supplier,
            simodo::inout::Reporter_abstract &                   reporter,
            simodo::interpret::SemanticDataCollector_interface & semantic_data_collector,
            simodo::interpret::InterpretType                     interpret_type,
            const std::string &                                  initial_contract_file) const;

    // Hover
    static std::u16string makeHoverText(const simodo::variable::Variable &) ;

    // Completion
    static simodo::variable::Value makeCompletionItem(const std::u16string & name,
                                                      const std::u16string & detail,
                                                      const std::u16string & description,
                                                      int64_t                kind) ;


    // SemanticTokens
    SemanticTokenType selectSemanticTokenType(const simodo::variable::Variable & var, SemanticTokenType function_index) const;


    // Общие методы

    /// Проверяет, находится ли данная позиция в semantic_data.remotes()
    bool isRemoteFunctionLaunch(const simodo::inout::TokenLocation & using_location) const;

    /// Находит токены начала и конца тела функции (фигурные скобки) и возвращает диапазон из них.
    static simodo::lsp::Range getFunctionBodyRange(const simodo::variable::Variable & var);

    /// Преобразование переменной в текст, подобный исходному коду.
    /// Например, "fn func(int : no) -> bool" или "int : num = 5".
    static std::u16string getVariableString(const simodo::variable::Variable & var);

    // Работа с токенами и их позициями

    /// Проверка, что локация токена в том же файле, что сейчас открыт.
    bool isInCurrentFile(const simodo::inout::TokenLocation & loc) const {
        return loc.uri_index() == _current_file_index;
    }

    /// Проверка, что позиция в том же файле и в пределах диапазона токена.
    bool isOnToken(const simodo::inout::TokenLocation & loc, const simodo::lsp::Position & pos) const {
        return isInCurrentFile(loc) and loc.range().contains(pos);
    }
};

#endif
